local header = {}

local function stringstrip(s)
	return s:gsub("^%s*(.-)%s*$", "%1")
end

local function get_file_name()
	return vim.fn.expand("%:t")
end

local function get_git_username()
	local result = io.popen("git config user.name")
	local name = result:read("*all")
	return stringstrip(name)
end

local function get_git_email()
	local result = io.popen("git config user.email")
	local email = result:read("*all")
	return stringstrip(email)
end

local function get_git_root()
	local path = vim.fn.expand("%:p:h")
	local cmd = "git -C " .. path .. " rev-parse --show-toplevel"
	local result = io.popen(cmd, "r")
	local git_root_path = stringstrip(result:read("*all"))
	local git_dir_name = string.match(git_root_path, "([^/\\]+)$")
	return git_dir_name or ""
end

local function read_line(num)
	local cur_buf = vim.api.nvim_get_current_buf()
	local result = vim.api.nvim_buf_get_lines(cur_buf, num - 1, num, false)
	return result[1]
end

local function replace_line(num, content)
	local cur_buf = vim.api.nvim_get_current_buf()
	vim.api.nvim_buf_set_lines(cur_buf, num - 1, num, false, { content })
end

local function add_line(num, content)
	local cur_buf = vim.api.nvim_get_current_buf()
	vim.api.nvim_buf_set_lines(cur_buf, num, num, false, { content })
end

header.config = {
	search_limit = 20,
	file_name = true,
	project_name = true,
	author = true,
	email = true,
	create_datetime = true,
	modified_datetime = true,
	datetime_fmt = "%Y-%m-%d %H:%M:%S",
	copyright = nil,
	license = nil,
}

local function file_create_datetime()
	local create_time = vim.fn.getftime(vim.fn.expand("%:p"))
	return os.date(header.config.datetime_fmt, create_time)
end

local function file_modified_datetime()
	return os.date(header.config.datetime_fmt)
end

header.format = {
	lua_like = {
		comment_start = "--[[",
		start_part = nil,
		comment_mark = "--",
		end_part = nil,
		comment_end = "--]]",
	},
	python_like = {
		comment_start = nil,
		start_part = nil,
		comment_mark = "#",
		end_part = nil,
		comment_end = nil,
	},
	c_like = {
		comment_start = "/*",
		start_part = nil,
		comment_mark = "*",
		end_part = nil,
		comment_end = "*/",
	},
}

header.language = {
	["c"] = header.format.c_like,
	["cpp"] = header.format.c_like,
	["python"] = header.format.python_like,
	["lua"] = header.format.lua_like,
}

header.run = {
	file_name = get_file_name,
	project_name = get_git_root,
	author = get_git_username,
	email = get_git_email,
	create_datetime = file_create_datetime,
	modified_datetime = file_modified_datetime,
}

local comment_fields = {}
comment_fields.general = {
	{ "File name:", "file_name", false },
	{ "Project name:", "project_name", false },
	{ "Author:", "author", true },
	{ "Email:", "email", false },
	{ "Create Datetime:", "create_datetime", true },
	{ "Modified Datetime:", "modified_datetime", false },
}
header.comment_fields = comment_fields

local function gen_header()
	local filetype = vim.bo.filetype
	local run = header.run
	local comment_format = header.language[filetype]
	if comment_format == nil then
		return false
	end
	local comment = header.comment_fields[filetype]
	if comment == nil then
		comment = header.comment_fields["general"]
	end
	local search_limit = header.config.search_limit
	local comment_mark = comment_format.comment_mark

	local linenum = 1
	local content = ""
	local comment_start = comment_format.comment_start
	if comment_start ~= nil then
		content = stringstrip(read_line(linenum))
		if content ~= comment_start then
			add_line(linenum - 1, comment_start)
		end
		linenum = linenum + 1
	end

	local start_part = comment_format.start_part
	if start_part ~= nil then
		for _, part in ipairs(start_part) do
			content = stringstrip(read_line(linenum))
			if content ~= part then
				add_line(linenum - 1, part)
			end
			linenum = linenum + 1
		end
	end

	for _, value in ipairs(comment) do
		local field_name = value[1]
		local value_name = value[2]
		local fixed = value[3]
		local pattern = ""
		content = stringstrip(read_line(linenum))
		if comment_mark then
			pattern = "^" .. comment_mark .. " " .. field_name
		else
			pattern = "^" .. field_name
		end
		if string.match(content, pattern) then
			if type(fixed) == "string" then
				replace_line(linenum, pattern:sub(2, -1) .. " " .. fixed)
			else
				if not fixed then
					local tmpfunc = run[value_name]
					local result = tmpfunc()
					replace_line(linenum, pattern:sub(2, -1) .. " " .. result)
				end
			end
		else
			if type(fixed) == "string" then
				add_line(linenum - 1, pattern:sub(2, -1) .. " " .. fixed)
			else
				local tmpfunc = run[value_name]
				local result = tmpfunc()
				add_line(linenum - 1, pattern:sub(2, -1) .. " " .. result)
			end
		end
		linenum = linenum + 1
	end

	local end_part = comment_format.end_part
	if end_part ~= nil then
		for _, part in ipairs(end_part) do
			content = stringstrip(read_line(linenum))
			if content ~= part then
				add_line(linenum - 1, part)
			end
			linenum = linenum + 1
		end
	end

	local comment_end = comment_format.comment_end
	if comment_end ~= nil then
		content = stringstrip(read_line(linenum))
		if content ~= comment_end then
			add_line(linenum - 1, comment_end)
		end
		linenum = linenum + 1
	end
end

local function autocmds()
	vim.api.nvim_create_user_command("GenHeader", function()
		header.gen_header()
	end, { complete = "file", nargs = "?", bang = true })
end

header.setup = function()
	autocmds()
end

header.gen_header = function()
	gen_header()
end

return header
